package org.opentcs.strategies.basic.routing.jgrapht;

import jakarta.inject.Inject;
import org.jgrapht.Graph;
import org.jgrapht.GraphPath;
import org.opentcs.components.kernel.routing.Edge;
import org.opentcs.data.TCSObjectReference;
import org.opentcs.data.model.Point;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.order.Route;
import org.opentcs.strategies.basic.routing.PointRouter;
import org.opentcs.strategies.basic.routing.jgrapht.jjwcalg.FSYAStartShortPathAlg;
import org.opentcs.strategies.basic.routing.jgrapht.jjwcalg.MyAStartAlg;
import org.opentcs.strategies.basic.scheduling.ReservationPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

public class DyPointRouter implements PointRouter {

  private static final Logger LOG = LoggerFactory.getLogger(DyPointRouter.class);
  private final GraphProvider graphProvider;
  private final ReservationPool reservationPool;
  private MyAStartAlg<String, Graph<String, Edge>> alg;
  private final Map<String, Point> points = new HashMap<>();

  @Inject
  public DyPointRouter(GraphProvider graphProvider, ReservationPool reservationPool) {
    this.graphProvider = graphProvider;
    this.reservationPool = reservationPool;
  }


  private Graph<String, Edge> getGraphForVehicle(Vehicle vehicle) {
    //底层帮我进行了拦截
    return graphProvider.getGraphResult(vehicle).getGraph();
  }

  @Override
  public List<Route.Step> getRouteSteps(Point srcPoint, Point destPoint) {

    return List.of();
  }

  public List<Route.Step> getRouteSteps(Point srcPoint, Point destPoint, Vehicle vehicle) {
    long timeBefore = System.currentTimeMillis();
    initAlg(vehicle);
    alg.setVehicleName(vehicle.getName());
    GraphPath<String, Edge> graphPath = alg.getPath(srcPoint.getName(), destPoint.getName());
    LOG.info("{} 重路由计算路由，起点：{}，终点：{}",vehicle.getName(),srcPoint.getName(),destPoint.getName());
    if (graphPath == null) {
      return List.of();
    }

    List<Route.Step> result = translateToSteps(graphPath);

    LOG.debug("Looking up route from {} to {} took {} milliseconds.",
        srcPoint.getName(),
        destPoint.getName(),
        System.currentTimeMillis() - timeBefore);

    return result;
  }

  @Override
  public long getCosts(TCSObjectReference<Point> srcPointRef, TCSObjectReference<Point> destPointRef) {
    return 0;
  }

  public long getCosts(TCSObjectReference<Point> srcPointRef, TCSObjectReference<Point> destPointRef, Vehicle vehicle) {
    initAlg(vehicle);
    alg.setVehicleName(vehicle.getName());
    GraphPath<String, Edge> path = alg.getPath(srcPointRef.getName(), destPointRef.getName());
    LOG.info("{} 重路由计算消耗，起点：{}，终点：{}",vehicle.getName(),srcPointRef.getName(),destPointRef.getName());
    if (path == null) {
      return Long.MAX_VALUE;
    }
    return (long) path.getWeight();
  }

  private void initAlg(Vehicle vehicle) {
    if (points.isEmpty()) {
      Set<Point> pointBase = graphProvider.getGraphResult(vehicle).getPointBase();
      for (Point point : pointBase) {
        points.put(point.getName(), point);
      }
    }
    if (alg != null)
      return;
    Graph<String, Edge> graphForVehicle = getGraphForVehicle(vehicle);
    alg = new MyAStartAlg<>(graphForVehicle, reservationPool);
  }

  @Override
  public boolean isRouble(Point startPoint) {
    return false;
  }

  private List<Route.Step> translateToSteps(GraphPath<String, Edge> graphPath) {
    List<Edge> edges = graphPath.getEdgeList();
    List<Route.Step> result = new ArrayList<>(edges.size());

    int routeIndex = 0;
    for (Edge edge : edges) {
      Point sourcePoint = points.get(graphPath.getGraph().getEdgeSource(edge));
      Point destPoint = points.get(graphPath.getGraph().getEdgeTarget(edge));

      result.add(new Route.Step(edge.getPath(),
          sourcePoint,
          destPoint,
          orientation(edge, sourcePoint),
          routeIndex));
      routeIndex++;
    }

    return result;
  }

  private Vehicle.Orientation orientation(Edge edge, Point graphSourcePoint) {
    return Objects.equals(edge.getPath().getSourcePoint(), graphSourcePoint.getReference())
        ? Vehicle.Orientation.FORWARD
        : Vehicle.Orientation.BACKWARD;
  }
}
